#include "ethernet2.h"
#include "usart1.h"

#include <stdlib.h>

#ifndef NULL
#define NULL			((void *)0)
#endif

#if ETHERNET2_HAVE_CRC
static unsigned int crc32_table[256] = {0};

static unsigned int reflect(unsigned int ref, unsigned char ch)
{	
	int i;
	unsigned int value = 0;
	for( i = 1; i < ch + 1; i++ )
	{
		if(ref & 0x1)
		{
			value |= 1 << (ch - i);
		}
		ref >>= 1;
	}
	return value;
}

static unsigned int ethernet2_crc32_count(unsigned char *data, unsigned int size)
{
	int i = 0;
	unsigned int crc = 0xFFFFFFFFU;
	for(i = 0; i < size; i++)
	{
		crc =  crc32_table[(crc ^ data[i]) & 0xFFU] ^ (crc >> 8);
	}
	return ~crc;
}

static void ethernet2_crc32_init(void)
{
	int i = 0, j = 0;
	for(i = 0; i <= 0xFF; i++) 
	{
		crc32_table[i] = reflect(i, 8) << 24;
		for(j = 0; j < 8; j++)
		{
			crc32_table[i] = (crc32_table[i] << 1) ^ (0 == (crc32_table[i] & 0x80000000U) ? 0x0U : 0x04C11DB7U);
		}
		crc32_table[i] = reflect(crc32_table[i], 32);
	}
}
#endif

static int ethernet2_alloc(ETHERNET2_INTERFACE_P obj, unsigned char *dest, unsigned short type, unsigned char *data, unsigned int len)
{
	int i = 0;
	obj->data_size = 46 <= len ? len : 46;
	obj->data = calloc(obj->data_size, 1);
	obj->data_type = type;
	for(i = 0; i < 6; i++)
	{
		obj->tomac[i] = dest[i];
	}
	obj->size = obj->data_size + ETHERNET2_HEAD_SIZE;
	for(i = 0; i < len; i++)
	{
		obj->data[i] = data[i];
	}
	obj->size = obj->data_size + ETHERNET2_HEAD_SIZE;
	return 0;
}

#if ETHERNET2_HAVE_DUMP
static void ethernet2_dump(ETHERNET2_INTERFACE_P obj)
{
	USART1_INTERFACE_P usart1 = NULL;
	usart1_get_interface(&usart1);

	usart1->send_crlf(1);
	usart1->send_string("--------------ethernet2-dump-start----------------");
	usart1->send_crlf(2);

	usart1->send_string("obj->data_size = ");
	usart1->send_int(obj->data_size, 0);
	usart1->send_crlf(2);

	usart1->send_string("obj->data_type = ");
	usart1->send_int(obj->data_type, 1);
	usart1->send_crlf(2);

	usart1->send_string("obj->tomac = ");
	usart1->send_array(obj->tomac, 6, sizeof(obj->tomac[0]), 2);
	usart1->send_crlf(2);

	usart1->send_string("obj->mymac = ");
	usart1->send_array(obj->mymac, 6, sizeof(obj->mymac[0]), 2);
	usart1->send_crlf(2);

	usart1->send_string("obj->size = ");
	usart1->send_int(obj->size, 0);
	usart1->send_crlf(2);

	usart1->send_string("--------------ethernet2-dump-stop----------------");
	usart1->send_crlf(2);
}
#endif

static int ethernet2_to_text(ETHERNET2_INTERFACE_P obj, unsigned char *buf, int len)
{
	int i = 0;
	unsigned char *data = NULL;
#if ETHERNET2_HAVE_CRC
	unsigned int crc = 0;
#endif

	if(NULL == obj || NULL == obj->data || obj->size >= len)
	{
		return -1;
	}

	for(i = 0; i < 12; i++)
	{
		if(6 > i)
		{
			buf[i] = obj->tomac[i];
		}
		else
		{
			buf[i] = obj->mymac[i - 6];
		}
	}
	buf[12] = obj->data_type >> 8;
	buf[13] = obj->data_type;
	data = &buf[14];
	for(i = 0; obj->data_size > i; i++)
	{
		data[i] = obj->data[i];
	}
#if ETHERNET2_HAVE_CRC
	data = &buf[obj->data_size + 14];
	crc = ethernet2_crc32_count(buf, obj->size - 4);
	for(i = 0; 4 > i; i++)
	{
		data[i] = ((unsigned char *)&crc)[i];
	}
#endif
	return 0;
}

static int ethernet2_free(ETHERNET2_INTERFACE_P obj)
{
	free(obj->data);
	return 0;
}

static ETHERNET2_INTERFACE_T _obj_ethernet2 = {
	.data = NULL,
	.data_size = 0,
	.mymac = {
		0x0,
		0x0,
		0x0,
		0x0,
		0x0,
		0x0
	},
	.alloc = ethernet2_alloc,
#if ETHERNET2_HAVE_DUMP
	.dump = ethernet2_dump,
#endif
	.tobuf = ethernet2_to_text,
	.free = ethernet2_free
};

void ethernet2_init(unsigned char *mac)
{
	int i = 0;
	for(i = 0; i < 6; i++)
	{
		_obj_ethernet2.mymac[i] = mac[i];
	}
#if ETHERNET2_HAVE_CRC
	ethernet2_crc32_init();
#endif
}

void ethernet2_get_interface(ETHERNET2_INTERFACE_P *obj)
{
	*obj = &_obj_ethernet2;
}